<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
namespace JfbCache\Aspect;

use Hyperfx\Framework\Logger\Logx;
use JfbCache\AnnotationManager;
use Hyperf\Cache\CacheManager;
use Hyperf\Cache\Driver\KeyCollectorInterface;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use JfbCache\Annotation\CacheableX;

class CacheableXAspect extends AbstractAspect
{
    public array $classes = [];

    public array $annotations = [
        CacheableX::class,
    ];

    public function __construct(protected CacheManager $manager, protected AnnotationManager $annotationManager)
    {
    }

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        $className = $proceedingJoinPoint->className;
        $method = $proceedingJoinPoint->methodName;
        $arguments = $proceedingJoinPoint->arguments['keys'];

        [$key, $ttl, $expiredTime, $group, $annotation] = $this->annotationManager->getCacheableValue($className, $method, $arguments);

        $driver = $this->manager->getDriver($group);

        [$has, $result] = $driver->fetch($key);
        if ($has && isset($result['expired_time']) && isset($result['data'])) {
            if ($result['expired_time'] == 0 || time() < $result['expired_time']) {
                return $result['data'];
            }
            // 重新获取，如果报错，从缓存里取
            try {
                $newResult = $proceedingJoinPoint->process();
            } catch (\Throwable $e) {
                Logx::get()->error(sprintf('handel %s:%s process error', $className, $method), [
                    'error_msg' => $e->getMessage(),
                ]);
                return $result['data'];
            }
            $result = $newResult;
        } else {
            $result = $proceedingJoinPoint->process();
        }

        $driver->set($key, [
            'data' => $result,
            'expired_time' => $expiredTime,
        ], $ttl);
        if ($driver instanceof KeyCollectorInterface && $annotation instanceof CacheableX && $annotation->collect) {
            $driver->addKey($annotation->prefix . 'MEMBERS', $key);
        }

        return $result;
    }
}
